/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Modele;

import java.awt.Point;
import java.util.ArrayList;

/**
 * Classe représentant l'environnement du jeu de la vie.
 * @author FERRIER/NONGLATON
 */
public class Environnement {

    private Cellule[][] lesCellules;
    private ArrayList<Cellule> cellulesVivantes;
    private int hauteur;
    private int largeur;
    private Regle regle;

    /**
     * Créé un environnement de base de 50x50.
     */
    public Environnement() {
        this(50, 50);
    }

    /**
     * Créé un environnement 
     * @param hauteur hauteur de l'environnement
     * @param largeur largeur de l'environnement
     */
    public Environnement(int hauteur, int largeur) {
        this.hauteur = hauteur;
        this.largeur = largeur;
        this.lesCellules = new Cellule[hauteur][largeur];
        initLesCellules();
        this.cellulesVivantes = new ArrayList<Cellule>();
        this.regle = new Regle();
    }

    /**
     * Permet la création d'un Environnement a partir d'une version simplifiée
     * @param versionSimple tableau de boolean représentant les cellules: <br>  - case true pour cellule vivante<br>  - case false pour cellule morte
     */
    public Environnement(boolean[][] versionSimple){
        this(versionSimple.length,versionSimple[0].length);
        for (int i = 0; i < hauteur; i++) {
            for (int j = 0; j < largeur; j++) {
                 lesCellules[i][j].setEtat(versionSimple[i][j]);
            }
        }
        
    }

    // <editor-fold defaultstate="collapsed" desc="Getters & Setters">
    /**
     * Retourne la hauteur de l'environnement
     * @return la hauteur de l'environnement
     */
    public int getHauteur() {
        return hauteur;
    }

    /**
     * Définit la hauteur de l'environnement.
     * @param hauteur hauteur de l'environnement
     */
    public void setHauteur(int hauteur) {
        this.hauteur = hauteur;
    }

    /**
     * Retourne la largeur de l'environnement.
     * @return la largeur de l'environnement
     */
    public int getLargeur() {
        return largeur;
    }

    /**
     * Définit la largeur de l'environnement.
     * @param largeur largeur de l'environnement
     */
    public void setLargeur(int largeur) {
        this.largeur = largeur;
    }

    /**
     * Retourne la règle de l'environnement.
     * @return la règle de l'environnement
     */
    public Regle getRegle() {
        return regle;
    }

    /**
     * Retourne la cellule de coordonnées (x,y).
     * @param x coordonnée verticale
     * @param y coordonnée horizontale
     * @return la cellule correspondant aux coordonnées (x,y)
     */
    public Cellule getCellule(int x, int y) {

        return lesCellules[x][y];

    }

    /**
     * Retourne les cellules vivantes.
     * @return le tableau des cellules vivantes
     */
    public ArrayList<Cellule> getCellulesVivantes() {

        return cellulesVivantes;
    }
// </editor-fold>

    /**
     * Initialise les cellules et référence leurs voisines
     */
    private void initLesCellules() {

        //initialisation des cellules
        for (int x = 0; x < hauteur; x++) {
            for (int y = 0; y < largeur; y++) {
                lesCellules[x][y] = new Cellule(false, new Point(x, y), this);

            }
        }

        //initialisation des voisins
        for (int x = 0; x < hauteur; x++) {
            for (int y = 0; y < largeur; y++) {
                Cellule[] voisins = new Cellule[8];

                //Ces conditions permettent de définir les voisins lorsque la cellule est sur un coté.
                int xInf = (x - 1 >= 0 ? x - 1 : x - 1 + hauteur);
                int yInf = (y - 1 >= 0 ? y - 1 : y - 1 + largeur);
                int xSup = (x + 1 < hauteur ? x + 1 : x + 1 - hauteur);
                int ySup = (y + 1 < largeur ? y + 1 : y + 1 - largeur);

                //voisins de gauche
                voisins[0] = lesCellules[xInf][yInf];
                voisins[1] = lesCellules[xInf][y];
                voisins[2] = lesCellules[xInf][ySup];
                //voisin au dessus et en dessous
                voisins[3] = lesCellules[x][yInf];
                voisins[4] = lesCellules[x][ySup];
                //voisin de droite
                voisins[5] = lesCellules[xSup][yInf];
                voisins[6] = lesCellules[xSup][y];
                voisins[7] = lesCellules[xSup][ySup];

                lesCellules[x][y].setVoisins(voisins);
                
            }
        }

    }

    /**
     * Retourne les coordonées des cellules vivantes
     * @return La liste des coordonnées des cellules vivantes
     */
    public ArrayList<Point> getCoordonneesCelluleVivantes() {
        ArrayList<Point> listeCoordonneesCelluleVivantes = new ArrayList<Point>();
        for (Cellule cel : cellulesVivantes) {
            listeCoordonneesCelluleVivantes.add(cel.getCoordonnees());
        }

        return listeCoordonneesCelluleVivantes;
    }

    /**
     * Permet d'obtenir la liste des cellules a calculer en se basant sur la liste des cellules vivantes
     * Problème d'optimisation remplacé par nouvelleGenerationParcours()
     * @return Liste des cellules dont il faut verifier l'état
     */
//    private ArrayList<Cellule> listeCalcul() {
//        ArrayList<Cellule> celluleACalculer = new ArrayList<Cellule>(cellulesVivantes);
//        for (Cellule celVivante : cellulesVivantes) {
//            for (Cellule cel : celVivante.getVoisins()) {
//                //Si la cellule existe déjà dans la liste des cellules vivantes et n'est pas encore dans la liste des cellules à calculer
//                //on ajoute cette cellule déjà créé à la listeif(!celluleACalculer.contains(cel))
//                if (!celluleACalculer.contains(cel)) {
//                    celluleACalculer.add(cel);
//                }
//
//            }
//        }
//
//        return celluleACalculer;
//    }

    /**
     * Calcul de la nouvelle génération par parcours du tableau de cellules
     */
    public void nouvelleGenerationParcours() {

        for (int i = 0; i < hauteur; i++) {
            for (int j = 0; j < largeur; j++) {
                lesCellules[i][j].survie();

            }
        }

        for (int i = 0; i < hauteur; i++) {
            for (int j = 0; j < largeur; j++) {
                lesCellules[i][j].changeEtat();

            }
        }


    }

    /**
     * Permet le calcul de la nouvelle génération en se basant sur la liste des vivants
     * Problème d'optimisation remplacé par nouvelleGenerationParcours()
     */
//    public void nouvelleGeneration() {
//
//        ArrayList<Cellule> celluleACalculer = listeCalcul();
//
//        //on fait le calcul des cellules
//        for (Cellule cel : celluleACalculer) {
//            cel.survie();
//        }
//        //on met a jour les etats des cellules calculées
//        for (Cellule cel : celluleACalculer) {
//            if (cel.changeEtat()) {//si la cellule change d'état
//                if (cel.getEtat()) {//si elle nait
//                    cellulesVivantes.add(cel);
//                } else {//si elle meurt
//                    cellulesVivantes.remove(cel);
//                }
//            }
//        }
//    }

    /**
     * Remplit aléatoirement l'environnement
     */
    public void generationAleatoire() {
        //initialisation des cellules
        for (int i = 0; i < hauteur; i++) {
            for (int j = 0; j < largeur; j++) {
                lesCellules[i][j].setEtat(Math.random() < 0.5);

            }
        }
    }

    /**
     * Créé une nouvelle cellule aux coordonnées (x,y)
     * @param x coordonnée verticale
     * @param y coordonnée horizontale
     */
    public void newCellule(int x, int y) {
        lesCellules[x][y].setEtat(true);
        cellulesVivantes.add(lesCellules[x][y]);


    }

    /**
     * Créé une nouvelle cellule aux coordonnées du point p
     * @param p coordonnées de la nouvelle cellule
     */
    public void newCellule(Point p) {
        cellulesVivantes.add(new Cellule(true, p, this));

    }

    /**
     * Créé un vaisseau aux coordonnées (x,y)
     * @param x coordonnée verticale
     * @param y coordonnée horizontale
     */
    public void newVaisseau(int x, int y) {
        int yGauche = (y - 1<0?largeur-1:y-1);
        int yDroite = (y + 1>largeur-1?0:y+1);
        int xHaut = (x - 1<0?hauteur-1:x-1);
        int xBas = (x + 1>hauteur-1?0:x+1);
        
        newCellule(xBas, yDroite);
        newCellule(x, yDroite);
        newCellule(xBas, y);
        newCellule(xHaut, yDroite);
        newCellule(x, yGauche);
    }

    /**
     * Vide l'environnement
     */
    public void Clear() {
        for (int i = 0; i < hauteur; i++) {
            for (int j = 0; j < largeur; j++) {
                lesCellules[i][j].setEtat(false);
            }
        }
    }
    
    /**
     * Retourne une version simplifiée de l'environnement sous la forme d'un tableau de booléens
     * @return Un tableau de booleéns<br/>Chaques case contient true pour une cellule vivante ou false pour une cellule morte
     */
    public boolean[][] versionSimplifiee(){
        boolean[][] versionSimple = new boolean[hauteur][largeur];
        for (int i = 0; i < hauteur; i++) {
            for (int j = 0; j < largeur; j++) {
                versionSimple[i][j] = lesCellules[i][j].getEtat();
            }
        }
        return versionSimple;
    }

}
